/***************************************************************
 * FILENAME:  LAB6.c
 *
 * CREATED: 10/15/2012 7:27:58 PM
 * AUTHOR:    zimmermane@msoe.edu <Evan Zimmerman>
 *            duerk@msoe.edu <Kevin Duer>
 * PROVIDES:  
**************************************************************/

#include <avr/io.h>        //to use named components on Atmega 32
#include <avr/interrupt.h> //will include the interrupt vectors
#include <stdbool.h>       //for boolean
#include <stdio.h>		   //for I/O functions
#include <string.h>		   //for the String class
#include <avr/sleep.h>	   //For the Avr sleep functions
#include "lcd3200.h"
#include "usart3200.h"
#include "helpers.h"
#include "adc3200.h"

// LCD update types
typedef enum LCDCommand {HEARTBEAT, TEMPERATURE} updateType;

// temperature packet
typedef struct TransmissionPacket{
	char sourceAddr;
	char destinationAddr;
	char tempH;
	char tempL;
} Tpacket;

static volatile bool heart;       //indicates whether the heart is on or off
static volatile bool update_heartBeat;   //used to indicate time to update heart icon
static volatile bool update_temp;   //used to indicate time to update temperature
static volatile bool sleep_enabled;    //used to enable the sleep function
static volatile bool to_sample;  //used to see if the machine can sample
static volatile char sleep_counter;
static volatile char temp_counter;
static volatile char sample_counter;

static volatile int adcVal;  // raw ADC value
static volatile int tempF; // temp in Fahrenheit
static volatile int tempC; // temp in Celsius


// set up a file stream which will allow us to use printf to call our lcd_send_data method
FILE lcd_str = FDEV_SETUP_STREAM(lcd_send_data, lcd_send_data, _FDEV_SETUP_RW);


/*
 * display_welcome
 * Purpose: To display the welcome screen on the LCD
 * Parameters: None
 * Returns: None
 */
void display_welcome(void){
	lcd_send_data(0x11); // make sure backlight is on
	lcd_send_data(0x0C); // clear the screen
	delay_ms(60);
	lcd_gotoxy(0,0);
	printf("CE3200 ZIGBEE");
	lcd_gotoxy(1,0);
	printf("WELCOME!");
}

/*
 * LCD_UPDATE
 * Purpose: Updates part of the LCD based on the passed command
 * Parameters: command - type of update we are performing
 * Returns: None
 */
void LCD_UPDATE(updateType command){
	lcd_send_data(0x11); // make sure backlight is on
	switch (command)
	{
		case HEARTBEAT:
			lcd_gotoxy(0,15);
			if(heart){
				heart = false;
				lcd_send_data(' ');	
			}
			else{
				heart = true;
				lcd_send_data('H');
			}				
			break;
		case TEMPERATURE:
			lcd_gotoxy(0,0);
			printf("%u F           ",(char)tempF); // degrees Fahrenheit
			lcd_gotoxy(1,0);
			printf("%u C           ",(char)tempC); // degrees Celsius
			break;
	}
}

/*
 * send_packet
 * Purpose: sends the data in a transmission packet over usart
 * Parameters: *packet - pointer to a transmission packet structure
 * Returns: None
 */
void send_packet(Tpacket *packet){	
	usart_send_data(packet->sourceAddr);
	usart_send_data(packet->tempH);
	usart_send_data(packet->tempL);
}

/*
 * main
 * Purpose: This will be the method which controls the whole system
 *			it will setup default values and enter into a forever
 *			loop to check set values
 * Parameters: None
 * Returns: None
 */
int main(void){
	
	Tpacket packet;
	//setup default values
	heart = false;
	update_heartBeat = true;
	update_temp = true;
	sleep_enabled = false;
	to_sample = true;
	sleep_counter = 0; // count to 240 interrupts for 2 minutes of system sleep
	temp_counter = 0;  // count to 10 interrupts for 5 seconds of temp display
	sample_counter = 0; //count to 10 interrupts for 5 second of sample display
	adcVal = 0;
	tempF = 0;
	tempC = 0;
	
	// allows the use of printf with our LCD
	stdin = stdout = &lcd_str;
	
	// setup timer1A to interrupt every 500ms
	OCR1A = 0x1E85; // 7813
	
	//start counting at 0
	TCNT1H = 0;
	TCNT1L = 0;
	
	//set Timer counter 1 Output Compare for interrupt
	TIMSK = (1<<OCIE1A);
	
	//enable INT0 pushbutton
	// turn INT0 pullup on (PD2)
	PORTD = PORTD | (1<<PD2);
	//turn on INT0 interrupt
	GICR = (1<<INT0);
	//sets to falling edge trigger for INT0
	MCUCR = (MCUCR & 0xF0) | (1<<ISC01);
	
	//initialize needed peripherals
	adc_init(1);       // initialize ADC on ADC1 (PINA1)
	usart_init();      // initialize the USART
	lcd_init();        // initialize the LCD
	display_welcome(); // display welcome screen for
	                   // 3 seconds at startup
	delay_ms(3000);
	
	DDRB = 0xFF; // pin used for MUX selection of USART destination (0=LCD,1=ZIGBEE)
	
	PORTB &= ~(1<<PB7);
	// initialize tccr1b: CTC mode, clk/1024 (starts the timer)
	TCCR1B = (1<<WGM12) | (1<<CS12) | (1<<CS10);
	
	// set the sleep mode to IDLE on sleep
	set_sleep_mode(SLEEP_MODE_IDLE);
	
	//Enable global interrupts
	sei();
	
	//forever loop to update and track program
	while(true){
		
		if(update_heartBeat){
			update_heartBeat = false;
			LCD_UPDATE(HEARTBEAT);
		}
		
		if(update_temp){
			update_temp = false;
			adc_start();
			adc_wait(); // wait here for conversion to finish (polling)
			
			//adcVal = (((int)get_adch())<<8); 
			//adcVal |= get_adcl();
			adcVal = get_adc(); // returns the entire ADC for simplicity
			
			// Fahrenheit = ADCval/2
			tempF = (adcVal>>1); 
			
			// Celsius = (Fahrenheit - 32) / 1.8
			// for sake of not using floating point numbers (slow), we will say the division is by 2
			tempC = ((tempF-32)>>1); 
			
			packet.sourceAddr = 0x0C;        // our unit's source address
			packet.tempH = (char)(tempF>>8); // get high byte of temperature
			packet.tempL = (char)(tempF);    // get low byte of temperature
		
			delay_ms(2);
			PORTB |= (1<<PB0);            // switch to packet destination
			delay_ms(5);
			
			send_packet(&packet);
			
			delay_ms(5);
			PORTB &= ~(1<<PB0);           // switch back to LCD destination
			delay_ms(2);
			
			LCD_UPDATE(TEMPERATURE);
		}
		
		cli();
		if(sleep_enabled){
			lcd_send_data(0x12); // make sure backlight is off
			// start the system sleep
			sleep_enable();
			sei();
			sleep_cpu();
		}
		else
			sleep_disable();
			
		sei();		
    }
}

/* 
 * TIMER1_COMPA_vect
 * Purpose: Handles sleep time and heartbeat and temperature updates
 */
ISR(TIMER1_COMPA_vect){
	if(!to_sample){
		sample_counter++; // count to 10 for 5 seconds of temp display
		if(sample_counter == 10){
			sample_counter = 0;
			
			//set Timer counter 1 Output Compare for no interrupt
			TIMSK &= ~(1<<OCIE1A);
			sleep_enabled = true;
		}
	}
	//check to see if the sleep function is enabled
	if(sleep_enabled){
		sleep_counter++; // count to 240 for 2 minutes of sleep
		if (sleep_counter == 240)
		//if (sleep_counter == 10) // dont want to wait, so change sleep to 5 seconds
		{
			sleep_counter = 0;
			sleep_enabled = false;
			update_temp = true;
		}
	}
	//check to see if sleep is disabled
	if(!sleep_enabled){
		temp_counter++; // count to 10 for 5 seconds of temp display
		if(temp_counter == 10){
			temp_counter = 0;
			sleep_enabled = true;
		}
		else{
			update_heartBeat = true; // update heartbeat every 500ms
		}			
	}
}

/*
 * INT0_vect
 * Purpose: This turns on/off sampling
 */
ISR(INT0_vect){
	//toggle the to_sample function
	//this will enable or disable the sampling on the system
	if(to_sample){
		to_sample = false;
		sample_counter = 0;
		sleep_counter = 0;
		temp_counter = 0;
		PORTB |= (1<<PB7);
	}
	else{
		to_sample = true;
		//set Timer counter 1 Output Compare for interrupt
		TIMSK |= (1<<OCIE1A);
		sleep_enabled = false;
		PORTB &= ~(1<<PB7);
	}
}